﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Input;

namespace OmniKits.Windows
{
    [TypeConverter(typeof(RelaxKeyGestureConverter))]
    public class RelaxKeyGesture
    {
        private static RelaxKeyGestureConverter RelaxKeyGestureConverter = new RelaxKeyGestureConverter();

        public Key Key { get; private set; }
        public ModifierKeys Modifiers { get; private set; }

        public RelaxKeyGesture(Key key, ModifierKeys modifiers = ModifierKeys.None)
        {
            Key = key;
            Modifiers = modifiers;
        }

        public RelaxKeyGesture(KeyGesture keyGesture)
            : this(keyGesture.Key, keyGesture.Modifiers)
        { }

        public static bool IsModifierKey(Key key)
        {
            switch (key)
            {
                case Key.LeftCtrl:
                case Key.LeftAlt:
                case Key.LeftShift:
                case Key.RightCtrl:
                case Key.RightAlt:
                case Key.RightShift:
                case Key.LWin:
                case Key.RWin:
                    return true;
                default:
                    return false;
            }
        }

        public static RelaxKeyGesture Parse(ITypeDescriptorContext context, CultureInfo culture, string text)
        {
            return (RelaxKeyGesture)RelaxKeyGestureConverter.ConvertFromString(context, culture, text);
        }
        public static RelaxKeyGesture Parse(ITypeDescriptorContext context, string text)
        {
            return (RelaxKeyGesture)RelaxKeyGestureConverter.ConvertFromString(context, text);
        }
        public static RelaxKeyGesture Parse(string text)
        {
            return (RelaxKeyGesture)RelaxKeyGestureConverter.ConvertFromString(text);
        }

        public override string ToString()
        {
            return RelaxKeyGestureConverter.ConvertToString(this);
        }
        public string ToString(ITypeDescriptorContext context)
        {
            return RelaxKeyGestureConverter.ConvertToString(context, this);
        }
        public string ToString(ITypeDescriptorContext context, CultureInfo culture)
        {
            return RelaxKeyGestureConverter.ConvertToString(context, culture, this);
        }

        public override int GetHashCode() { return base.GetHashCode(); }
        public bool Equals(RelaxKeyGesture b)
        {
            if (object.ReferenceEquals(b, null)) return false;
            return this.Key == b.Key && this.Modifiers == b.Modifiers;
        }
        public override bool Equals(object obj)
        {
            return this.Equals(obj as RelaxKeyGesture);
        }

        public static bool operator ==(RelaxKeyGesture a, RelaxKeyGesture b)
        {
            if (object.ReferenceEquals(b, null))
                return object.ReferenceEquals(a, null);
            return a.Equals(b);
        }
        public static bool operator !=(RelaxKeyGesture a, RelaxKeyGesture b)
        {
            return !(a == b);
        }
    }

    public class RelaxKeyGestureConverter : TypeConverter
    {
        private static readonly KeyConverter KeyConverter = new KeyConverter();
        private static readonly KeyGestureConverter KeyGestureConverter = new KeyGestureConverter();
        private static readonly ModifierKeysConverter ModifierKeysConverter = new ModifierKeysConverter();

        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return sourceType == typeof(RelaxKeyGesture) || sourceType == typeof(KeyGesture) || sourceType == typeof(string);
        }
        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            if (value == null)
                return null;

            var mkg = value as RelaxKeyGesture;
            if (mkg != null) return mkg;

            var kg = value as KeyGesture;
            if (kg != null)
                return new RelaxKeyGesture(kg.Key, kg.Modifiers);

            var s = value as string;
            if (s == null)
                throw new NotSupportedException();

            if (string.IsNullOrWhiteSpace(s))
                return null;

            var pos = s.LastIndexOfAny(new[] { '+', '|' });
            Key key;
            ModifierKeys modifiers = ModifierKeys.None;
            if (pos < 0)
                key = (Key)KeyConverter.ConvertFrom(context, culture, s);
            else
                key = (Key)KeyConverter.ConvertFrom(context, culture, s.Substring(pos + 1));

            if (RelaxKeyGesture.IsModifierKey(key))
                return new RelaxKeyGesture(Key.None, (ModifierKeys)ModifierKeysConverter.ConvertFrom(context, culture, s));
            else if (pos >= 0)
                modifiers = (ModifierKeys)ModifierKeysConverter.ConvertFrom(context, culture, s.Substring(0, pos));

            return new RelaxKeyGesture(key, modifiers);
        }

        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            return destinationType == typeof(RelaxKeyGesture) || destinationType == typeof(KeyGesture) || destinationType == typeof(string);
        }
        private static readonly Regex FormatRegex = new Regex(@"\s*[+,]\s*");
        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            var mkg = (RelaxKeyGesture)value;

            if (destinationType == typeof(RelaxKeyGesture))
                return mkg;

            if (destinationType == typeof(KeyGesture))
            {
                if (mkg == null) return null;
                return new KeyGesture(mkg.Key, mkg.Modifiers);
            }

            if (destinationType != typeof(string))
                throw new NotSupportedException();

            if (mkg == null) return null;

            var noKey = mkg.Key == Key.None;
            var noMod = mkg.Modifiers == ModifierKeys.None;
            string s;

            if (noKey)
            {
                if (noMod)
                    return "None";
                else
                    s = ModifierKeysConverter.ConvertToString(context, culture, mkg.Modifiers);
            }
            else
            {
                s = KeyConverter.ConvertToString(context, culture, mkg.Key);

                if (!noMod)
                    s = ModifierKeysConverter.ConvertToString(context, culture, mkg.Modifiers) + "+" + s;
            }

            return FormatRegex.Replace(s, " + ");
        }
    }
}
